using SqlSugar;
using IM.Easy.Core.Services;
using IM.Easy.Infrastructure.Query;
using IM.Easy.Core.Extensions;
using IM.Easy.Entity.System;
using IM.Easy.SystemServices.Dtos;
using IM.Easy.SystemServices.IServices;
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics.Metrics;
using System.Collections.Generic;
using IM.Easy.Core.Utils;
using Mapster;

namespace IM.Easy.SystemServices.Services
{
    public class FolderService : AppServices<Folder>, IFolderService
    {
        private readonly string defaultFolderName = "新建文件夹";
        public ResourcesDto Root => new ResourcesDto
        {
            Name = "根目录",
            Virtual = false,
        };


        public FolderService(ISqlSugarClient db) : base(db)
        {

        }

        public Folder Create(Folder entity)
        {
            entity.FolderName = GetInitFolderName(entity.Pid);
            return Context.Insertable(entity).ExecuteReturnEntity();
        }

        public Folder GetById(Guid id)
        {
            // TODO: 添加获取逻辑
            return Context.Queryable<Folder>().InSingle(id);
        }

        public PageResponse<Folder> GetPageList(FolderQueryDto query)
        {
            var queryable = Context.Queryable<Folder>();
            var exp = Expressionable.Create<Folder>();
            exp = exp
;
            var page = queryable
                 .Where(exp.ToExpression())
                 .ToPageList<Folder, Folder>(query);
            return page;
        }

        public bool Update(Folder entity)
        {
            // TODO: 添加更新逻辑
            return Context.Updateable(entity).ExecuteCommandHasChange();
        }

        public bool Delete(params Guid[] Id)
        {
            return Context.Deleteable<Folder>(Id).ExecuteCommandHasChange();
        }

        /// <summary>
        /// 删除文件夹下面的文件夹
        /// </summary>
        /// <param name="fid"></param>
        /// <returns></returns>
        public void DeleteByFolder(Guid fid)
        {
            Context.Deleteable<Folder>()
            .Where(s => s.Pid == fid)
            .ExecuteCommand();
        }

        /// <summary>
        /// 获取文件夹中所有内容(包含自己)  非递归数据
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public List<ResourcesDto> GetSubContents(params Guid[] ids)
        {
            var idStrArray = ids.Select(i => i.ToString()).ToArray();
            // 查询文件夹
            var folderQueryable = Context.Queryable<Folder>()
                .Select(f => new ResourcesDto
                {
                    Id = f.Id,
                    Name = f.FolderName,
                    Pid = f.Pid,
                    Type = "folder",
                    Icon = "folder",
                    SubType = "folder",
                    SubTypeName = "文件夹",
                    PhysicalPath = string.Empty,
                    UpdatedAt = f.UpdatedAt,
                    Size = 0
                }, true);
            // 查询文件
            var fileQueryable = Context.Queryable<FileResource>()
               .LeftJoin<FileTypes>((fr, ft) => fr.Mime == ft.MimeType)
                  .Select((fr, ft) => new ResourcesDto
                  {
                      Id = fr.Id,
                      Name = fr.FileName,
                      Pid = fr.FolderId,
                      Type = ft.Group,
                      Icon = ft.Icon,
                      SubType = ft.MimeType,
                      SubTypeName = ft.TypeName,
                      PhysicalPath = fr.FilePath,
                      UpdatedAt = ft.UpdatedAt,
                      Size = fr.FileSize,
                  }, true);

            // 合并查询（UnionAll 避免去重，提高查询效率）
            var resources = Context.UnionAll(folderQueryable, fileQueryable)
                         .LeftJoin<SysUser>((f, u) => f.CreatedBy == u.Id)
                         .Select((f, u) => new ResourcesDto { CreatedByName = u.NickName, Url = "" }, true)
                     .ToChildList(f => f.Pid, idStrArray);
            return resources;
        }

        /// <summary>
        /// 获取文件夹中所有内容(包含自己)  非递归数据
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public List<ResourcesDto> GetSubContents(FolderQueryDto query)
        {
            // 查询文件夹
            var folderQueryable = Context.Queryable<Folder>()
                .Select(f => new ResourcesDto
                {
                    Id = f.Id,
                    Name = f.FolderName,
                    Pid = f.Pid,
                    Type = "folder",
                    Icon = "folder",
                    SubType = "folder",
                    SubTypeName = "文件夹",
                    PhysicalPath = string.Empty,
                    UpdatedAt = f.UpdatedAt,
                    Size = 0
                }, true);
            // 查询文件
            var fileQueryable = Context.Queryable<FileResource>()
               .LeftJoin<FileTypes>((fr, ft) => fr.Mime == ft.MimeType)
                  .Select((fr, ft) => new ResourcesDto
                  {
                      Id = fr.Id,
                      Name = fr.FileName,
                      Pid = fr.FolderId,
                      Type = ft.Group,
                      Icon = ft.Icon,
                      SubType = ft.MimeType,
                      SubTypeName = ft.TypeName,
                      PhysicalPath = fr.FilePath,
                      UpdatedAt = ft.UpdatedAt,
                      Size = fr.FileSize,
                  }, true);

            // 合并查询（UnionAll 避免去重，提高查询效率）
            var resources = Context.UnionAll(folderQueryable, fileQueryable)
                         .LeftJoin<SysUser>((f, u) => f.CreatedBy == u.Id)
                         .Where(f => f.Name.Contains(query.Keyword))
                         .Select((f, u) => new ResourcesDto { CreatedByName = u.NickName, Url = "" }, true)
                     .ToChildList(f => f.Pid, query.FolderId);
            return resources;
        }



        /// <summary>
        /// 获取文件夹下面的所有内容
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public ResourcesDto GetContents(FolderQueryDto query)
        {
            // 查询文件夹
            var folderQueryable = Context.Queryable<Folder>()
                .Where(f => f.Pid == query.FolderId)
                .Select(f => new ResourcesDto
                {
                    Id = f.Id,
                    Name = f.FolderName,
                    Pid = f.Pid,
                    Type = "folder",
                    Icon = "folder",
                    SubType = "folder",
                    SubTypeName = "文件夹",
                    PhysicalPath = string.Empty,
                    UpdatedAt = f.UpdatedAt,
                    Url = "",
                    Size = 0,
                }, true);

            // 查询文件
            var fileQueryable = Context.Queryable<FileResource>()
                .LeftJoin<FileTypes>((fr, ft) => fr.Mime == ft.MimeType)
                .Where(fr => fr.FolderId == query.FolderId)
                .Select((fr, ft) => new ResourcesDto
                {
                    Id = fr.Id,
                    Name = fr.FileName,
                    Pid = fr.FolderId,
                    Type = ft.Group,
                    Icon = ft.Icon,
                    SubType = ft.MimeType,
                    SubTypeName = ft.TypeName,
                    PhysicalPath = fr.FilePath,
                    UpdatedAt = ft.UpdatedAt,
                    Url = "",
                    Size = fr.FileSize,
                }, true);

            // 合并查询（UnionAll 避免去重，提高查询效率）
            var content = Context.UnionAll(folderQueryable, fileQueryable)
                     .LeftJoin<SysUser>((f, u) => f.CreatedBy == u.Id)
                     .Select((f, u) => new ResourcesDto { CreatedByName = u.NickName }, true)
                     .ToList();
            var folder = GetById(query.FolderId);
            var result = folder.Adapt<ResourcesDto>() ?? Root;
            result.Content = content;
            var parents = Context.Queryable<Folder>().ToParentList(f => f.Pid, query.FolderId);
            result.Path = parents.Adapt<List<ResourcesDto>>();
            result.Path.Insert(0, Root);
            return result;
        }

        /// <summary>
        /// 重命名
        /// </summary>
        /// <param name="id"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool Rename(Guid id, string name)
        {
            if (!NameValidate(name, out string errorMsg))
            {
                ExceptionUtils.ThrowBusinessException(errorMsg);
            }
            var folder = GetById(id);

            var duplicate = IsAny(s => s.Pid == folder.Pid && name == s.FolderName);
            if (duplicate) ExceptionUtils.ThrowBusinessException("已存在相同命名文件夹");
            folder.FolderName = name;
            Context.Updateable(folder)
                .UpdateColumns(s => s.FolderName)
                .ExecuteCommandHasChange();
            return true;
        }

        /// <summary>
        /// 文件夹命名合法性校验
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool NameValidate(string name, out string errorMessage)
        {
            int maxLength = 10;
            if (string.IsNullOrWhiteSpace(name))
            {
                errorMessage = "名称不能为空";
                return false;
            }

            if (name.Length > maxLength)
            {
                errorMessage = "名称过长，建议10个字符内";
                return false;
            }

            // 初始化错误信息为空
            errorMessage = string.Empty;
            // 获取不允许出现在文件名中的字符数组
            char[] invalidChars = Path.GetInvalidFileNameChars();

            // 使用 LINQ 检查名称中是否包含不允许的字符
            if (name.IndexOfAny(invalidChars) != -1)
            {
                string invalidCharList = string.Join(", ", invalidChars.Select(c => $"'{c}'"));
                errorMessage = $"文件夹名称包含不允许的字符: {invalidCharList}。";
                return false;
            }

            // 所有检查都通过，名称合法
            return true;
        }


        /// <summary>
        /// 获取初始化文件夹名称
        /// </summary>
        /// <param name="pid"></param>
        /// <returns></returns>
        private string GetInitFolderName(Guid? pid)
        {
            var folderName = defaultFolderName;

            // 获取同一父目录下的所有文件夹名称
            var existingNames = Context.Queryable<Folder>()
                                      .Where(f => f.Pid == pid)
                                      .Select(f => f.FolderName)
                                      .ToList();

            string baseName = defaultFolderName;
            int counter = 0;

            // 生成唯一的文件夹名称
            while (existingNames.Contains(folderName))
            {
                counter++;
                folderName = $"{baseName}{counter}";
            }
            return folderName;
        }

    }
}
